/*
 * Anything: A flexible and easy to use data structure that combines the 
 * characteristics of a list and a hashmap into one data structure.
 * 
 * Based on a paper by Peter Sommerlad and Marcel Rueedi on 
 * 'Do-it-yourself reflection' entered at EuroPLOP 1998.
 * 
 * This library was written completely from scratch and is based only 
 * in spirit on the original Java Anything implementation written by 
 * Andre Weinand/Marcel Rueedi in 1997/99 and enhanced by Stefan Tramm 
 * in 2006.
 * 
 * Copyright (c) 2008 Thomas Marti and others. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package any.functor.impl.func;

import any.Any;
import any.ImmutableAny;
import any.functor.Functions;
import any.functor.interfaces.AsAny;
import any.functor.interfaces.AsSimpleAny;
import any.functor.interfaces.Function;
import any.incubator.IterableConfig;

public class AsAnyImpl 
	extends BaseAnyIteratingAnyCreatingFunction<Any, Any, Object, AsAnyImpl> 
	implements AsAny, 
	           AsSimpleAny {

	private Boolean asMutable;
	private final boolean asSimpleAny;

	public AsAnyImpl() {
		super(Any.class);
		this.asSimpleAny = false;
	}

	public AsAnyImpl(boolean asSimpleAny) {
		super(Any.class);
		this.asSimpleAny = asSimpleAny;
	}

	public Any apply(ImmutableAny from) {
		boolean mutable;
		if (asMutable != null) {
			mutable = asMutable;
		//TODO: is this needed? for type ImmutableAny this always returns false
		} else if (!from.onlySequentialAccess()) {
			mutable = from.isMutable();
		} else {
			mutable = true;
		}
		
		Any any = ImmutableAny.create.any(mutable, entries(from));

		if (asSimpleAny && !any.isSimpleType()) {
			throw new IllegalStateException(any + " is not simple!");
		}
		return any;
	}
	
	public AsAnyImpl eachElement(Function<? super ImmutableAny, ?> asFunc) {
		this.asFunc = wrapFunc(asFunc);
		return this;
	}

	public AsSimpleAny convert(Function<? super ImmutableAny, ?> asFunc) {
		return eachElement(asFunc);
	}
	
	public AsAnyImpl mutable(boolean mutable) {
		this.asMutable = mutable;
		return this;
	}

	public AsAnyImpl immutable() {
		this.asMutable = false;
		return this;
	}
	
	private Function<ImmutableAny, Any> wrapFunc(final Function<? super ImmutableAny, ?> func) {
		if (func == null) {
			return Functions.toAny();
		} else {
			return new Function<ImmutableAny, Any>() {
				public Any apply(ImmutableAny from) {
					return Any.create.any(func.apply(from.toAny()));
				}
				@Override public String toString() {
				    return "to any from " + func;
				}
			};
		}
	}
	
	@Override
	public String toString() {
		String result = "As";
		
		result += (asMutable == Boolean.FALSE ? "Immutable" : "Mutable");
		result += (asSimpleAny ? "Simple" : "");
		result += "Any";
		
		IterableConfig iterableConfig = getIterableConfig();
		if (!iterableConfig.isDefault()) {
	        result += " with " + iterableConfig.toString();
        }
		
		if (asFunc != null) {
			if (asSimpleAny) {
				result += ", convert " + asFunc + "; ";
			} else {
				result += ", each " + asFunc + "; ";
			}
		}
		
	    return result;
	}

}
